package so.wy.servicecenter.businessbed.manager.normal.wx;

import com.alibaba.fastjson.JSON;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import so.wdl.framework.core.codec.Base64;
import so.wy.servicecenter.base.exception.ManagerException;
import so.wy.servicecenter.base.frame.manager.AbstractManager;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;

@Service
@Scope("prototype")
public class WxToolNManager extends AbstractManager {
    @Value("${wx.miniapp.appid}")
    private String appId;
    @Value("${wx.miniapp..secret}")
    private String secret;

    private final String WX_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={1}&secret={2}&" +
            "js_code={3}&grant_type=authorization_code";

    /**
     * 获取微信Session
     *
     * @param jsCode 客户端秘钥
     * @return {@link WxSessionBean}
     * @author NO
     * @since 2019.01.18
     */
    public WxSessionDTO getWxProCode2Session(String jsCode) throws  ManagerException {
        RestTemplate restTemplate = new RestTemplate();

        WxSessionBean wxSessionBean;

        try {
            ResponseEntity<String> entity = restTemplate.getForEntity(WX_SESSION_URL, String.class, appId, secret, jsCode);
            wxSessionBean = JSON.parseObject(entity.getBody(), WxSessionBean.class);
        } catch (RestClientException e) {
            throw exception("微信网络请求异常");
        }

        if (wxSessionBean == null) {
            throw exception("微信数据获取异常");
        }

        if (wxSessionBean.getErrcode() != null && wxSessionBean.getErrcode() != 0) {
            logger.error("获取微信小程序的session报错：" + wxSessionBean.getErrcode() + ":" + wxSessionBean.getErrMsg());
            throw exception("微信数据获取异常");
        }

        return new WxSessionDTO(wxSessionBean.getOpenId(),wxSessionBean.getSession_key(),wxSessionBean.getUnionid());
    }

    /**
     * 微信小程序用户信息解密
     *
     * @param encryptedData 加密数据
     * @param sessionKey    session Key
     * @param iv            IV
     * @return {@link WxDecodeDTO}
     * @author NO
     * @since 2019.01.28
     */
    public WxDecodeDTO decrypt(String encryptedData, String sessionKey, String iv) throws  ManagerException {
        String userInfo = getUserInfo(encryptedData, sessionKey, iv);
        if (userInfo == null) {
            throw exception("微信数据解析异常");
        }

        return JSON.parseObject(userInfo, WxDecodeDTO.class);
    }

    /**
     * 解密微信信息
     *
     * @param encryptedData 加密数据
     * @param sessionKey    session Key
     * @param iv            IV
     * @return 解密数据Json
     * @author NO
     * @since 2019.01.28
     */
    private String getUserInfo(String encryptedData, String sessionKey, String iv) {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);

        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }

            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                return new String(resultByte, "UTF-8");
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        return null;
    }
}
